﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using PriceLibrary.PriceService;
using VDataAdapter.PriceServerAdapter;

namespace VDataAdapter.Communication
{
    /// <summary>
    /// Concat message old & new and control flag etc
    /// </summary>
    class ProtocolMessageBuilder
    {
        IVdataMessageBuilder _builder;

        DataSource _dataSource;

        private Object _marketInfoLock = new Object();

        private Object _ptTransactionLock = new Object();

        public ProtocolMessageBuilder(IVdataMessageBuilder builder, DataSource dataSource){
            
            if (builder == null || dataSource == null)
                throw new NullReferenceException("Cant work with null MessageBuilder or DataSource");
            
            _builder = builder;
            _dataSource = dataSource;
        }

        public string BuildMessageTransactionPutThrough(Transaction transaction)
        {
            return BuildMessageTransactionPutThrough(transaction, _dataSource.PtTransactions);
        }

        private string BuildMessageTransactionPutThrough(Transaction transaction, IDictionary<string, Transaction> dataSourcePutThroughTransactions)
        {
            string result;
            
            lock (_ptTransactionLock)
            {
                if (dataSourcePutThroughTransactions.ContainsKey(transaction.Symbol))
                {
                    result = BuildMessageTransactionPutThrough(transaction, dataSourcePutThroughTransactions[transaction.Symbol]);
                }
                else
                {
                    result = BuildMessageTransactionPutThrough(transaction, transaction);
                }

                _dataSource.UpdatePtTransaction(transaction);
            }

            return result;
        }

        /// <summary>
        /// Build the message Buy Put Through struct included values and properties of new Stock Buy and old Stock Buy
        /// </summary>
        /// <param name="objStatic"></param>
        /// <param name="objNew"></param>
        /// <param name="objOld"></param>
        /// <returns>Return a message string have struct along Stick Buy parameter</returns>
        public string BuildMessagePutThrough(PriceLibrary.PriceService.StockInfoStatic objStatic, PriceLibrary.PriceService.StockInfoDyns objNew, PriceLibrary.PriceService.StockInfoDyns objOld, TransactionType transType)
        {
            throw new NotImplementedException();
        }

        public string BuildMessageTransactionPutThrough(PriceLibrary.PriceService.Transaction objNew,
        PriceLibrary.PriceService.Transaction objOld)
        {
            string strMsgObj = "PUTEXEC#";
            string strNew = String.Empty;
            string strOld = String.Empty;
            int FloorCode = 10;
            ///Get FloorCode From datasource.BasePrices base on Transaction.Symbol
            try
            {
                var stockstatic = (PriceLibrary.PriceService.StockInfoStatic)_dataSource.BasePrices[objNew.Symbol];
                FloorCode = stockstatic.FloorCode;
            }
            catch (Exception ex)
            {
                throw new Exception("Cant get floor code", ex);
            }
            //Build Msg 
            try
            {
                //New----------------------------------------------------------
                strNew += FloorCode.ToString("00") + "|";
                strNew += objNew.Symbol + "|";
                strNew += objNew.Last + "|";
                strNew += objNew.LastVol + "|";
                //strNew += objNew.Time + "|";
                strNew += Convert.ToInt32(objNew.Time).ToString("000000") + "|";
                //Old----------------------------------------------------------                
                strOld += FloorCode + "|";
                strOld += objOld.Symbol + "|";
                strOld += objOld.Last + "|";
                strOld += objOld.LastVol + "|";
                //strOld += objNew.Time + "|";
                strOld += Convert.ToInt32(objOld.Time).ToString("000000") + "|";
                ///Phân cách giữa Msg mới và Msg cũ bằng dấu #
                strMsgObj += strNew + "#" + strOld;
            }
            catch (Exception ex)
            {
                throw new Exception("Error building message", ex);
            }
            return strMsgObj;
        }

        public string BuildMessageMarketInfo(int key, MarketInfo marketInfo)
        {
            string msg;

            lock (_marketInfoLock)
            {
                try
                {
                    if (_dataSource.MarketInfos.ContainsKey(key))
                    {
                        var oldMarketInfo = (MarketInfo)_dataSource.MarketInfos[key];
                        msg = _builder.BuildMessageMarket(marketInfo, oldMarketInfo);
                    }
                    else
                    {
                        msg = _builder.BuildMessageMarket(marketInfo, marketInfo);                        
                    }

                    _dataSource.UpdateMarketInfo(marketInfo);
                }
                catch(Exception ex)
                {
                    throw new MessageBuildingException("There's something wrong with building message", ex);
                }
            }
            return msg;
        }

        public void UpdateBasePrices(IDictionary<string, PriceLibrary.PriceService.StockBase> basePrices)
        {
            foreach (KeyValuePair<string, PriceLibrary.PriceService.StockBase> p in basePrices)
            {
                var infoStatic =
                    (PriceLibrary.PriceService.StockInfoStatic)p.Value;
                _dataSource.UpdateStockBase(infoStatic);
            }
        }         

        public string BuildMessageStockInfo(string key, PriceLibrary.PriceService.StockInfoDyns dynamicInfo)
        {
            string msg;

            lock(_dataSource.Top3Prices)
            {
                try
                {
                    msg = BuildMessageStockInfo(key, dynamicInfo,         
                        TypeConvertor.StockBaseToStockInfoDyns(_dataSource.Top3Prices),                        
                        TypeConvertor.StockBaseToStockInfoStatic(_dataSource.BasePrices));

                    _dataSource.UpdateStockDynamic(dynamicInfo);
                }
                catch (InvalidCastException ex)
                {
                    throw new InvalidCastException("BuildMessageStockInfo cant convert list of top3prices & baseprices" + ex.Message);
                }
            }

            return msg;
        }
        
        public string BuildMessageTransaction(PriceLibrary.ChartService.Transaction transaction)
        {
            string msg;

            // if DataSource already contain this Transaction, send new transaction along with the old
            msg = BuildMessageTransaction(transaction, _dataSource.Transactions);
            // update DataSource anyway
            _dataSource.UpdateTransaction(transaction);
            
            return msg;
        }

        public string BuildMessagePutThroughTopPrices(string key, PriceLibrary.PriceService.StockInfoDyns stock)
        {
            string msg;
            try
            {
                var objStatic = (PriceLibrary.PriceService.StockInfoStatic)_dataSource.BasePrices[key];

                if (!IsSellPutThrough(stock))
                {

                    if (_dataSource.PtTopPriceBuy.ContainsKey(key))
                    {
                        var oldStock = (PriceLibrary.PriceService.StockInfoDyns)_dataSource.PtTopPriceBuy[key];
                        msg = _builder.BuildMessagePutThrough(objStatic, stock, oldStock, TransactionType.Buy);
                    }
                    else
                    {
                        msg = _builder.BuildMessagePutThrough(objStatic, stock, stock, TransactionType.Buy);
                    }

                    _dataSource.UpdatePutTopPriceBuy(stock);
                }
                else
                {
                    if (_dataSource.PtTopPriceSell.ContainsKey(key))
                    {
                        var oldStock = (PriceLibrary.PriceService.StockInfoDyns)_dataSource.PtTopPriceSell[key];
                        msg = _builder.BuildMessagePutThrough(objStatic, stock, oldStock, TransactionType.Sell);
                    }
                    else
                    {
                        msg = _builder.BuildMessagePutThrough(objStatic, stock, stock, TransactionType.Sell);
                    }

                    _dataSource.UpdatePutTopPriceSell(stock);
                }
            }
            catch (InvalidCastException ex)
            {
                throw new InvalidCastException("Cant convert DataSource: " + ex.Message, ex);
            }
            catch (Exception ex)
            {
                throw new Exception("Unknown exception protocol message builder: ", ex);
            }

            
            return msg;
        }

        #region private functions
        private string BuildMessageStockInfo(string key, PriceLibrary.PriceService.StockInfoDyns stockInfo,
           IDictionary<string, PriceLibrary.PriceService.StockInfoDyns> top3Prices,
           IDictionary<string, PriceLibrary.PriceService.StockInfoStatic> basePrices)
        {
            var basePrice = basePrices[key];

            if (top3Prices.ContainsKey(key))
            {
                var stockInfoOld = top3Prices[key];
                return _builder.BuildMessageStock(basePrice, stockInfo, stockInfoOld);
            }
            else
            {
                return _builder.BuildMessageStock(basePrice, stockInfo, stockInfo);
            }
        }
        
        private bool IsSellPutThrough(PriceLibrary.PriceService.StockInfoDyns stock)
        {
            if (stock.Best1BidVolume > 0) return false;

            if (stock.Best1OfferVolume > 0) return true;

            throw new ArgumentException("Can't determine is a sell or buy PutThrough", "stock");            
        }

        //private string BuildMessageMarketInfo(int key, PriceLibrary.PriceService.MarketInfo marketInfo)
        //{
        //    if (!_dataSource.MarketInfos.ContainsKey(key))
        //        return _builder.BuildMessageMarket(marketInfo, marketInfo);
        //    else
        //        return _builder.BuildMessageMarket(marketInfo, (PriceLibrary.PriceService.MarketInfo)_dataSource.MarketInfos[key]);
        //}

        private string BuildMessageTransaction(PriceLibrary.ChartService.Transaction transaction, IDictionary<string, PriceLibrary.ChartService.Transaction> transactions)
        {
            string msg;
            if (IsOldTransactionExisted(transaction))
            {
                msg = _builder.BuildMessageTransaction(transaction, transactions[transaction.Symbol]);
            }
            else
            {
                msg = _builder.BuildMessageTransaction(transaction, transaction);
            }
            return msg;
        }

        private bool IsOldTransactionExisted(PriceLibrary.ChartService.Transaction transaction)
        {
            return _dataSource.Transactions.ContainsKey(transaction.Symbol);
        }
        #endregion

    }
}
